#include "utils.h"
#include "cache.h"

/*============================================================================
SeparaCadena()
============================================================================*/
void SeparaCadena(char *sParametro,char *sValor,char *cadena) {

   int		iConta1=0,iConta2=0;

   strcpy(sParametro,cadena);

   while(sParametro[iConta1]!='='){
         iConta1++;
	}

	sParametro[iConta1]='\0';
    iConta1++;
    while(cadena[iConta1]!='\n') {
        sValor[iConta2]=cadena[iConta1];
	    iConta1++;iConta2++;
	 }

    sValor[iConta2]='\0';
}

/*============================================================================
cargarConfiguracion()
Lee el archivo de configuracion y carga los datos a la estructura del PFS
============================================================================*/

int cargarConfiguracion() {

	FILE		*pArch;
	char		sParam[60],sValor[60],Aux[60];


	memset(Aux,'\0',60);

	if ((pArch = fopen("./PFSconfig.txt", "r"))==NULL) {
		return 0;
	}

	while (!feof(pArch)) {
		fgets(Aux,60,pArch);
		SeparaCadena(sParam,sValor,Aux);

		if(strcmp(sParam,"IP")==0)
			strcpy(stPFS.szIp,sValor);
		if(strcmp(sParam,"PUERTO")==0)
			stPFS.iPuerto = atoi(sValor);
		if(strcmp(sParam,"IP_REMOTO")==0)
			strcpy(stPFS.szIpRemoto,sValor);
		if(strcmp(sParam,"PUERTO_REMOTO")==0)
			stPFS.iPuertoRemoto = atoi(sValor);
		if(strcmp(sParam,"MAXIMAS_CONEXIONES")==0)
			stPFS.iMaxConexiones = atoi(sValor);
		if(strcmp(sParam,"TAMANIO_CACHE")==0)
			stPFS.iTamCache = atoi(sValor);
		if(strcmp(sParam,"SECTORES_BLOQUE")==0)
			stPFS.iSectoresxBloque = atoi(sValor);

	}
	fclose(pArch);
	return 1;
}

/*============================================================================
iniciarLog()
Abre el archivo log y escribe el encabezado. Recibe como parametros:
- *nombreLog: nombre del archivo log a crearse.
============================================================================*/
void iniciarLog( const char *nombreLog )
{
	FILE		*pArchLog;
	time_t		tiempo;
	struct tm	*ptm;

	time(&tiempo);
	ptm = localtime(&tiempo );

	pArchLog = fopen(nombreLog,"a");

	fprintf(pArchLog, "Fecha\t  NombreProceso\t[PIDproceso]  [ThreadID]:TipoLog Dato\n");
	fprintf(pArchLog, "************************************************************\n");

	fclose(pArchLog);
}

/*============================================================================
loguear()
Abre el archivo log y escribe al final. Recibe como parametros:
- *nombreLog: nombre del archivo log.
- *name: puntero al nombre del proceso.
- *type: tipo de mensaje (ERROR, INFO, etc).
- *log: puntero a la cadena, que contiene el mensaje a escribir en el archivo.
============================================================================*/
void loguear( const char *nombreLog, const char *name, const char *type, const char *log)
{
	FILE		*pArchLog;
	time_t		tiempo;
	struct tm	*ptm;

	time(&tiempo);
	ptm = gmtime(&tiempo );

	pArchLog = fopen(nombreLog,"a");

	fprintf(pArchLog, "[%2d:%02d:%02d]  [%s]\t[%d]\t\t[%d]\t[%s]\t[%s]\n",ptm->tm_hour, ptm->tm_min, ptm->tm_sec, name, getpid(), pthread_self(), type, log);
/*%2d:%02d:%04d - ptm->tm_mday, ptm->tm_mon+1, ptm->tm_year+1900, */
	fclose(pArchLog);
}

/*============================================================================
procesaComando()
============================================================================*/
int procesaComando() {

	char		szBuferConsola[30];
	char		*comando, *parametro;
	int8_t		comandoInvalido = 0;
	uint32_t	uTamanioFAT, clustersLibres, totalClusters;

	memset(szBuferConsola,'\0',30);

	fgets(szBuferConsola,30,stdin);

	/* Borrar fin de línea, si lo hay. */

	if (szBuferConsola[strlen(szBuferConsola) - 1] == '\n')
		szBuferConsola[strlen(szBuferConsola) - 1] = '\0';

	comando = strtok(szBuferConsola," ");

	if( comando != NULL) {

		if (strcmp(comando, "fsinfo") == 0) {
			// verifico que no haya parametros
			if (strtok(NULL," ") != NULL) {
				comandoInvalido = 1;
			} else {
				totalClusters = bootSector.totalSectors32 / bootSector.sectorPorCluster;
				clustersLibres = cantidadClustersLibres(totalClusters);

				printf("\nCantidad de cluster ocupados: %u \nCantidad de cluster libres: %u \n", totalClusters-clustersLibres, clustersLibres);
				printf("Tamanio de un sector: %u bytes\n",bootSector.bytesPorSector);
				printf("Tamanio de un cluster: %u bytes\n",(bootSector.bytesPorSector*bootSector.sectorPorCluster));
				uTamanioFAT = (bootSector.sectorPorFAT32*bootSector.bytesPorSector)/1024;
				printf("Tamanio de la FAT: %u Kbytes\n\n",uTamanioFAT);
				loguear( "pfsLog.txt", "PFS", "INFO", "Se respondio al comando fsinfo");
			}
		}
		else if (strcmp(comando, "finfo") == 0) {
			parametro = strtok(NULL," ");
			// verifico que haya uno y solo un parametro
			if (parametro == NULL || strtok(NULL," ") != NULL) {
				comandoInvalido = 1;
			} else {
				ST_FILE_ENTRY fileEntry;
				int iSocket = generarSocketNuevaConexion();

				if (getFileEntry(parametro, &fileEntry, iSocket)) {
					if (fileEntry.dataEntry.attributes == 0x20)
						printf("\n#Clusters:\n %s\n\n", imprimirClusters(fileEntry));
					else
						printf("\nNo es un archivo. \n\n");
				}
				else
					printf("\nEl archivo no existe. \n\n");

				loguear( "pfsLog.txt", "PFS", "INFO", "Se respondio al comando finfo");
				cerrarSocketNuevaConexion(iSocket);
			}
		}
		else if (strcmp(comando, "help") == 0) {
			// verifico que no haya parametros
			if (strtok(NULL," ") != NULL) {
				comandoInvalido = 1;
			} else {
				printf("Comandos disponibles: fsinfo | finfo [path_a_un_archivo] | exit\n\n");
			}
		}
		else if (strcmp(comando, "exit") == 0) {
			// verifico que no haya parametros
			if (strtok(NULL," ") != NULL) {
				comandoInvalido = 1;
			} else {
				return -2; // salir desde la consola
			}
		}
		else {
			comandoInvalido = 1;
		}
	}
	else {
		comandoInvalido = 1;
	}

	if (comandoInvalido) {
		printf("Comando erroneo, ingrese help para mas informacion\n\n");
	}

	return 0;
}

/*============================================================================
conexionAlmacenamiento()
============================================================================*/
int conexionAlmacenamiento () {
	struct sockaddr_in	stDireccion;
	int					iYes=1, iRta, iSocketRemoto;
	ST_NIPC				stCabecera;

	if((iSocketRemoto = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		loguear( "pfsLog.txt", "PFS", "ERROR", "Error al crear el socket de comunicacion - 1" );
		perror("error creando socket 1");
		return -2;
	} else {
		if(setsockopt(iSocketRemoto, SOL_SOCKET, SO_REUSEADDR, &iYes, sizeof(int)) == -1) {
			loguear( "pfsLog.txt", "PFS", "ERROR", "Error al crear el socket de comunicacion - 2" );
			perror("error creando socket 2");
			return -2;
		}
	}

	stDireccion.sin_addr.s_addr = inet_addr( stPFS.szIpRemoto );
	stDireccion.sin_family = AF_INET;
	stDireccion.sin_port = htons( stPFS.iPuertoRemoto );
	memset( &(stDireccion.sin_zero), '\0', 8 );

	iRta = connect( iSocketRemoto, (struct sockaddr *) &stDireccion, sizeof(struct sockaddr));

	if (iRta == -1){
		loguear( "pfsLog.txt", "PFS", "ERROR", "al intentar conectarse con el dispositivo de almacenamiento - 3" );
		perror("connect");
		close( iSocketRemoto );
	 	return -2;
	}

	stCabecera.ucType = HANDSHAKE;
	stCabecera.lPayload = 0;


	if( send( iSocketRemoto, &stCabecera, sizeof(ST_NIPC), MSG_NOSIGNAL ) < 0 ){
		close( iSocketRemoto );
		loguear( "pfsLog.txt", "PFS", "ERROR","Al enviar el mensaje Hand Shake");
		perror("send");
		return -3;
	}

	if( recv( iSocketRemoto, (void *) &stCabecera, sizeof(ST_NIPC), MSG_WAITALL) < 0 ) {
		close( iSocketRemoto );
		loguear( "pfsLog.txt", "PFS", "ERROR","Al recibir respuesta del Hand Shake");
		perror("recv");
		return -4;
	}


	 // TODO: se debe hacer un close del socket remoto???
	switch ( stCabecera.ucType ) {
		case FAIL:
			loguear( "pfsLog.txt", "PFS", "WARN","Conexion rechaza");
			printf("Conexion rechazada\n");
			return -1;
		case PPD:
			loguear( "pfsLog.txt", "PFS", "INFO","Conexion con PPD establecida");
			//printf("Conexion con PPD establecida\n");
			break;

		case PRAID:
			loguear( "pfsLog.txt", "PFS", "INFO","Conexion con PRAID establecida");
			//printf("Conexion con PRAID establecida\n");
			break;
	}


	stPFS.iSocketRemoto = iSocketRemoto;
	return iSocketRemoto;
}


/*============================================================================
trataSenial()
============================================================================*/
void trataSenial (int numeroSenial) {

	if( stPFS.iTamCache != 0 ) {
		if ( crearCacheDump() ) {
			loguear( "pfsLog.txt", "PFS", "INFO", "se ha atendido la senial SIGUSR1" );
			printf("se ha atendido la senial SIGUSR1\n" );
		} else {
			loguear( "pfsLog.txt", "PFS", "ERROR", "al atender la senial SIGUSR1" );
			printf("error al atender la senial SIGUSR1\n" );
		}
	} else
		printf("El proceso no tiene asociada una cache\n");

}

/*============================================================================
finPFS()
============================================================================*/
void finPFS(void) {
	if (sem_destroy (&semaforoConexiones) == -1)
	{
		printf("El semaforoConexiones  no pudo ser destruido\n");
	}
	if (sem_destroy (&semaforoListaCache) == -1)
	{
		printf("El semaforoListaCache no pudo ser destruido\n");
	}
	if (sem_destroy (&semaforoTablaFAT) == -1)
	{
		printf("El semaforoTablaFAT no pudo ser destruido\n");
	}
}

/*============================================================================
imprimirFat()
============================================================================*/
void imprimirFat(int cant) {
	int i;
	for( i = 0; i < cant; i++ ) {
		printf("FAT entrada %d: - %x\n",i, fatTable[i]);
	}
}

/*============================================================================
generarSocketNuevaConexion()
============================================================================*/
int generarSocketNuevaConexion() {
	int iSocket;

	if (sem_wait(&semaforoConexiones) == -1) // si el semaforo es 0, bloquea al proceso hasta que se incremente el semaforo
	{
		perror("Semáforo no válido");
		exit(1);
	}

	return conexionAlmacenamiento();
}

/*============================================================================
cerrarSocketNuevaConexion()
============================================================================*/
void cerrarSocketNuevaConexion(int iSocket) {
	close( iSocket );

	if (sem_post(&semaforoConexiones) == -1)
	{
		perror("Semáforo no válido");
		exit(1);
	}
}
